import { DynamicModule, Global, Module, Provider } from '@nestjs/common'
import { NotifyClientService } from './notify-client.service'
import {
  NotifyClientModuleAsyncOptions,
  NotifyClientModuleOptions,
  NotifyClientModuleOptionsFactory,
} from './interfaces/notify-client-options.interface'
import { NOTIFY_CLIENT_OPTIONS } from './notify-client.constants'

@Module({
  providers: [ NotifyClientService ],
  exports: [ NotifyClientService ],
})
@Global()
export class NotifyClientModule {

  public static forRoot(config: NotifyClientModuleOptions): DynamicModule {
    return {
      module: NotifyClientModule,
      providers: [
        {
          provide: NOTIFY_CLIENT_OPTIONS,
          useValue: config,
        },
      ],
      exports: [ NOTIFY_CLIENT_OPTIONS ],
    }
  }

  public static forRootAsync(options: NotifyClientModuleAsyncOptions): DynamicModule {
    return {
      module: NotifyClientModule,
      imports: options.imports,
      providers: this.createAsyncProviders(options),
      exports: [ NOTIFY_CLIENT_OPTIONS ],
    }
  }

  private static createAsyncProviders(options: NotifyClientModuleAsyncOptions): Provider[] {
    if (options.useExisting || options.useFactory) {
      return [ this.createAsyncOptionsProvider(options) ]
    }

    return [
      this.createAsyncOptionsProvider(options),
      {
        useClass: options.useClass!,
        provide: options.useClass!,
      },
    ]
  }

  private static createAsyncOptionsProvider(options: NotifyClientModuleAsyncOptions): Provider {
    if (options.useFactory) {
      return {
        provide: NOTIFY_CLIENT_OPTIONS,
        useFactory: options.useFactory,
        inject: options.inject || [],
      }
    }

    return {
      provide: NOTIFY_CLIENT_OPTIONS,
      useFactory: async (optionsFactory: NotifyClientModuleOptionsFactory) => (
        await optionsFactory.createNotifyClientOptions()
      ),
      inject: [ (options.useExisting || options.useClass)! ],
    }
  }

}
